// eee.Sheffield.PZ.Imaging
//
// Copyright ?Ping Zou, 2007
// sg71.cherub@gmail.com

namespace eee.Sheffield.PZ.Imaging
{
    using System;
    using System.IO;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Collections.Generic;
    using eee.Sheffield.PZ.Math;
    using System.Collections;

    /// <summary>
    /// try Liu&Sun tracking
    /// </summary>
    public class TaskTryLiuSunTracking
    {
        private PZPoint _initcp = new PZPoint();	// center point
        private PZDirection _initvd = new PZDirection();		// vessel direction
        private SunScanline _sl = new SunScanline();		// scanline
        private double _initr;
        private double _gamaThreshold;  // percent dynamic signal range threshold
        private double _Kd;             
        private int _d;		// forward step d = Kd * r
        private string _exitMsg;	// exit message

        /// <summary>
        /// Apply Filter
        /// </summary>
        /// <param name="srcImg"></param>
        /// <returns></returns>
        public Bitmap Apply(Bitmap srcImg)
        {
            int width = srcImg.Width;
            int height = srcImg.Height;
            Bitmap dstImg = new Bitmap(srcImg);
            Bitmap srcImageCopy = new Bitmap(srcImg);

            // -- prepare tracking
            double startx = 28;
            double starty = 14;
            double startdx = 0;
            double startdy = 1;
            _initcp = new PZPoint(startx, starty);
            _initvd = new PZDirection(startdx, startdy);
            _initr = 5.0;
            _Kd = 1.0;
            _d = (int)(_Kd * _initr);
            _d = 1;
            _gamaThreshold = 0.02;
            // prepare candidates arrays
            List<PZPoint> cpCandidates = new List<PZPoint>();
            List<PZDirection> vdCandidates = new List<PZDirection>();                        
            List<double> initrCandidates = new List<double>();

            cpCandidates.Add(_initcp);
            vdCandidates.Add(_initvd);
            initrCandidates.Add(_initr);

            // prepare last arrays
            List<LiuSunTrackResult> trLast = new List<LiuSunTrackResult>();            
            List<LiuSunTrackResult> trResults = new List<LiuSunTrackResult>();

            // do scanline tracking
            //try
            //{
                while (cpCandidates.Count > 0)
                {
                    trLast.Clear();

                    for (int i = 0; i < cpCandidates.Count; i++)
                    // for each cp candidate
                    {
                        System.Console.WriteLine("Tracking {0} of {1} candidates", i + 1, cpCandidates.Count);

                        PZPoint cpCurrent = new PZPoint(cpCandidates[i] as PZPoint);
                        PZDirection vdCurrent = new PZDirection(vdCandidates[i] as PZDirection);

                        System.Console.Write("Start from " + cpCurrent.ToString() + " ");
                        System.Console.WriteLine("with direction of " + vdCurrent.ToString());

                        int widthOfScanline = (int)System.Math.Round(initrCandidates[i] * 2 + 4);
                        double initr = (double)initrCandidates[i];
                        // prepare scanline
                        _sl = new SunScanline(initr, _gamaThreshold);

                        LiuSunTracking cs = new LiuSunTracking(cpCurrent, vdCurrent, _sl, _d);

                        // -- prepare Array collection to save tracking result
                        LiuSunTrackResult tr = new LiuSunTrackResult();

                        int count = 0;
                        //bool exit = false;

                        // prepare scanline analysis reord file
                        string slRecordFileName = "scanlineRecord.txt";
                        FileStream slRecordFileStream = new FileStream(slRecordFileName, FileMode.Append, FileAccess.Write);
                        StreamWriter slRecordStreamWriter = new StreamWriter(slRecordFileStream);
                        //slRecordStreamWriter.WriteLine("******** " + (count + 1) + " ********");
                        slRecordStreamWriter.WriteLine(String.Format(
                            "{0, -25}{1, -25}{2, -25}{3, -25}{4, -25}{5, -7}{6, -7}{7, -7}{8, -7}{9, -7}{10, -25}{11, -7}{12, -7}",
                            "P(k)", "u(k)", "p~(k + d)", "p'(k + d)", "u(k + d)", "S(k+d)", "B(k+d)", "EP1", "EP2",
                            "r", "p(k + d)", "gama", "IsFit"));
                        slRecordStreamWriter.Close();
                        slRecordFileStream.Close();

                        while (true)
                        {
                            count++;
                            System.Console.Write(count + ": ");
                            
                            cs.Apply(srcImageCopy);
                            cs.LastScanline.MemCopyFrom(cs.CurrentScanline);
                            cs.CurrentScanline = new SunScanline(cs.LastScanline.Radius, _gamaThreshold);

                            System.Console.WriteLine(String.Format(
                                "{0,-25}{1, -25}{2, -8:f}{3, -8:f}{4, -8:f}{5, -8:f}",
                                cs.CentrePoint.ToString(), cs.VesselDirection.ToString(),
                                cs.LastScanline.Centre, cs.LastScanline.Radius,
                                cs.LastScanline.SignalLevel, cs.LastScanline.BackgroundLevel));

                            // collect tracking result
                            tr.Add(new PZPoint(cs.CentrePoint), new PZPoint(cs.EdgePoint1), 
                                new PZPoint(cs.EdgePoint2), cs.LastScanline, new PZDirection(cs.VesselDirection));                            
                            
                            // tracking end criteria
                            if (!cs.IsFit)
                            {
                                _exitMsg = cs.ExitMsg;
                                System.Console.WriteLine(_exitMsg);
                                break;
                            }

                            // record on output image
                            if (cs.CentrePoint.x >= 0 && cs.CentrePoint.x < dstImg.Width
                                && cs.CentrePoint.y >= 0 && cs.CentrePoint.y < dstImg.Height
                                && cs.EdgePoint1.x >= 0 && cs.EdgePoint1.x < dstImg.Width
                                && cs.EdgePoint1.y >= 0 && cs.EdgePoint1.y < dstImg.Height
                                && cs.EdgePoint2.x >= 0 && cs.EdgePoint2.x < dstImg.Width
                                && cs.EdgePoint2.y >= 0 && cs.EdgePoint2.y < dstImg.Height)
                            {
                                dstImg.SetPixel((int)cs.CentrePoint.x, (int)cs.CentrePoint.y, Color.Red);
                                dstImg.SetPixel((int)cs.EdgePoint1.x, (int)cs.EdgePoint1.y, Color.Green);
                                dstImg.SetPixel((int)cs.EdgePoint2.x, (int)cs.EdgePoint2.y, Color.Blue);
                                string dstImageFileName = "intermedia image" + count + ".bmp";
                                dstImg.Save(dstImageFileName, ImageFormat.Bmp);
                            }
                        } // while

                        // add to last arrays
                        if (count >= 2)
                        {                                                       
                            // 'delete' tracked result
                            LiuSunDeletion del = new LiuSunDeletion(tr);
                            del.Apply(ref srcImageCopy);
                            // save srcImage after 'delete'
                            srcImageCopy.Save("delete src image.bmp", ImageFormat.Bmp);

                            trLast.Add(tr);
                        }

                        trResults.Add(tr);
                    } // for

                    System.Console.WriteLine("All {0} candidates tracking stopped", cpCandidates.Count);                    

                    cpCandidates.Clear();
                    vdCandidates.Clear();                    
                    initrCandidates.Clear();

                    
                    // search for new candidates
                    for (int i = 0; i < trLast.Count; i++)
                    // for each last track
                    {
                        LiuSunTrackResult tr = (LiuSunTrackResult)trLast[i];
                        LiuSunBifurcationSearch bifurcationSearch = new LiuSunBifurcationSearch(tr);
                        bifurcationSearch.Apply(srcImageCopy);
                        int candidate = bifurcationSearch.CentrePointCandidateArray.Count;
                        if (candidate != 0)
                        {
                            System.Console.WriteLine("return {0} candidates", candidate);
                            cpCandidates.AddRange(bifurcationSearch.CentrePointCandidateArray);
                            vdCandidates.AddRange(bifurcationSearch.VesselDirectionCandidateArray);
                            initrCandidates.AddRange(bifurcationSearch.InitialRadiusCandidateArray);
                        }
                    }
                } // while
            //} // try
            //catch (ApplicationException e)
            //{
            //    System.Console.WriteLine("exit consequtive scanline fitting");
            //}
            //finally
            //{
            //    for (int i = 0; i < trResults.Count; i++)
            //    {
            //        string trackResultFilename = "Trackresult_" + i.ToString() + ".txt";
            //        (trResults[i] as LiuSunTrackResult).Write(trackResultFilename);
            //    }
            //    System.Console.WriteLine("continue the bifurcation search");
            //}

            // -- return dstImg
                srcImageCopy.Dispose();
            return dstImg;
        }
    }
}
